MQTTメッセージでLambdaからブラウザを更新する 〜Vue+Amplify編
1 はじめに
CX事業本部の平内(SIN)です。
以前、下記のような記事を書きました。 こちらは、ブラウザ上で、aws-iot-device-sdkを使用して、AWS IoT Coreのメッセージブローカーをsubscribeし、特定のTopicが到着した時点で、ブラウザを更新するものでした。
やってることは、全く同じなのですが、今回は、これをAWS Amplify(以下、Amplify)で作成してみました。使用したウェブフレームワークは、Veu.jsです。
Amplifyでは、ライブラリAWSIoTProviderを使用することで、簡単にPub/Subが可能です。
Amplify Libraries AWSIoTProvider
また、権限については、AmplifyにAuthカテゴリを追加することで、CognitoのFederated Identitiesが利用可能なので、未認証IDを許可して、特定のトピックをSubscribeするだけのポリシーを追加しています。
「データ更新等に伴い表示を書き換える」というような要件だと、Amplify+AppSyncでGraphQLのSubscriptionで書くと、綺麗に仕上がりそうですが、今回は、あくまでMQTTと飛ばすことを目的として作業していることを、ご了承ください。
2 Vue.js
使用したVue CLIは、Ver 4.5.12 でした。
% vue --version @vue/cli 4.5.12
プリセットにデフォルトの Default ([Vue 2] babel, eslint) を指定してプロジェクト()を生成しています。
% vue create browser_refresh_sample ? Please pick a preset: (Use arrow keys) ❯ Default ([Vue 2] babel, eslint) Default (Vue 3 Preview) ([Vue 3] babel, eslint) Manually select features ・・・略・・・ ? Successfully created project browser_refresh_sample. ? Get started with the following commands: $ cd browser_refresh_sample $ yarn serve
この後、プロジェクトのフォルダ内で作業進めます。
% cd browser_refresh_sample
3 Amplify
amplifyの方は、4.50.0となっています。
% amplify --version 4.50.0
amplifyの初期化の方も、全てデフォルト設定です。
% amplify init ? Enter a name for the project browserrefreshsample ? Initialize the project with the above configuration? Yes Using default provider awscloudformation ? Please choose the profile you want to use developer ・・・略・・・ Your project has been successfully initialized and connected to the cloud! ・・・略・・・
続いて、authカテゴリを追加してpushしました。
% amplify add auth ❯ Default configuration ❯ Username ❯ No, I am done.
% amplify push ・・・略・・・ ✔ All resources are updated in the cloud % amplify status Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ---------------------------- | --------- | ----------------- | | Auth | browserrefreshsample0ec00635 | No Change | awscloudformation |
Cloud Formationのコンソールを確認するとベースのスタックと、Authカテゴリのスタックが作成されていることを確認できます。
4 ポリシー
Authカテゴリで追加されたスタックのリソース情報からIdentittyPoolのリンクを辿り、CognitoのIDプールの画面を開きます。
「認証されていないIDに対してアクセスを有効にする」にチェックを入れます。
未認証時に適用されるロールに、必要最小限の権限付与ということで、クライアントID、browser_refresh_sample_idからの接続と、トピックbrowser_refresh_sample_topicのサブスクライブと受信だけを許可しています。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iot:Connect" ], "Resource": [ "arn:aws:iot:ap-northeast-1:*:client/browser_refresh_sample_id" ] }, { "Effect": "Allow", "Action": [ "iot:Subscribe" ], "Resource": [ "arn:aws:iot:ap-northeast-1:*:topicfilter/browser_refresh_sample_topic" ] }, { "Effect": "Allow", "Action": [ "iot:Receive" ], "Resource": [ "arn:aws:iot:ap-northeast-1:*:topic/browser_refresh_sample_topic" ] } ] }
5 コード
最初に、ライブラリAWSIoTProviderを使用できるように、モジュールをインストールしています。
npm i aws-amplify @aws-amplify/pubsub npm i core-js@latest
Vue.jsのApp.vueは、下記のように書き換えました。mounted: でMQTTをサブスクライブしています。
<template> <div id="app"> {{ time }} </div> </template> <script> import awsconfig from './aws-exports' Amplify.configure(awsconfig) import Amplify, { PubSub } from 'aws-amplify'; import { AWSIoTProvider } from '@aws-amplify/pubsub/lib/Providers'; Amplify.addPluggable(new AWSIoTProvider({ aws_pubsub_region: 'ap-northeast-1', aws_pubsub_endpoint: 'wss://xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com/mqtt', clientId: 'browser_refresh_sample_id' })); export default { name: 'App', data: () => { return { time: "" } }, mounted: async function () { PubSub.subscribe('browser_refresh_sample_topic').subscribe({ next: data => { console.log('Message received', data) //location.reload() }, error: error => console.error(error), close: () => console.log('Done'), }); }, created : function(){ let currentdate = new Date() this.time = currentdate.getHours() + ':' + currentdate.getMinutes() + ':' + currentdate.getSeconds() + ' update.' } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
Topicにメッセージを送ってみると、ブラウザ側で反応していることが確認できます。
Topicが受信できていることを確認できたら、location.reload()を有効にして、トピック到着で、ブラウザをリロードします。
mounted: async function () { PubSub.subscribe('browser_refresh_sample_topic').subscribe({ next: data => { console.log('Message received', data) // ↓この行を有効にする location.reload() }, error: error => console.error(error), close: () => console.log('Done'), }); },
6 Lambda
Topicを送信するために作成したLambdaは、以下の通りです。Lambdaの権限に、IoT CoreへのPublishの権限追加が必要です。
exports.handler = async (event) => { // TODO implement const endpoint = 'xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com'; const topic = "browser_refresh_sample_topic" const aws = require('aws-sdk'); const region = 'ap-northeast-1'; var iotdata = new aws.IotData({ endpoint: endpoint, region: region }); var params = { topic: topic, payload: '{"action":"refresh"}', qos: 0 }; let result = await iotdata.publish(params).promise(); console.log(result); };
7 最後に
今回は、MQTTのPub/SubでLambdaとブラウザの連携を確認してみました。最初に、話した通り、情報の更新に伴う、画面の更新であれば、MQTTをわざわざ利用する必要なありませんが、MQTTでも、色々連携できれば、応用範囲が広がるように感じています。